<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style>
        html,
        body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
            font-family: Open Sans;
            font-size: 12px;
            color: orange;
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
            display: -webkit-flex;
            -webkit-align-items: center;
            -webkit-justify-content: center;
            background: #222;
        }
        h3 {
            margin: 0 0 3px 1px;
        }
        canvas {
            background: black;
        }
        span {
            width: 80px;
            margin: 0;
            padding: 0;
            display: inline-block;
        }
        form,
        label {
            display: inline-block;
        }
        input[type="range"] {
            margin: 0;
            padding: 0;
            width: 432px;
            display: inline;
            vertical-align: middle;
        }
    </style>
</head>
<body>
<div>
    <h3>Fourier series</h3>
    <canvas width="512" height="256"></canvas>
    <div>
        <span>Frequency</span>
        <input frequency type="range" min="-5" max="-3" value="-3.5" step="0.0001">
    </div>
    <div>
        <span>Order</span>
        <input order type="range" min="0" max="16" value="4" step="1">
    </div>
    <div>
        <span>Waveform</span>
        <form id="waveform">
            <label>Square
                <input type="radio" name="waveform" value="square" checked>
            </label>
            <label>Sawtooth
                <input type="radio" name="waveform" value="sawtooth">
            </label>
        </form>
    </div>
</div>
</body>
</html>

<script>
    var frequencyInput = document.querySelector("input[frequency]");
    var orderInput = document.querySelector("input[order]");
    var waveformInput = document.getElementById("waveform").elements["waveform"];
    var canvas = document.querySelector("canvas");
    var context = canvas.getContext("2d");

    var PI2 = Math.PI * 2.0;
    var Scale = 64.0;
    var time = 0.0;
    var startTime = new Date().getTime();
    var values = [];
    var valuePointer = 0;
    var x = 128.0,
            y = 128.0;

    function fourier(order) {
        var phase = order * time * PI2;
        var radius = 4.0 / (order * Math.PI) * Scale;
        context.beginPath();
        context.lineWidth = 1.0;
        context.strokeStyle = "rgba(255,128,32,1.0)";
        context.arc(x, y, radius, 0, PI2);
        context.stroke();
        context.strokeStyle = "rgba(255,255,255,0.4)";
        context.moveTo(x, y);
        x += Math.cos(phase) * radius;
        y += Math.sin(phase) * radius;
        context.lineTo(x, y);
        context.stroke();
    };

    function connect() {
        context.beginPath();
        context.moveTo(x + 0.5, y + 0.5);
        context.lineTo(256 + 0.5, y + 0.5);
        context.strokeStyle = "rgba(255,255,32,1.0)";
        context.stroke();
    };

    function drawWave() {
        values[valuePointer++ & 255] = y;
        context.beginPath();
        context.strokeStyle = "rgba(0,255,0,1)";
        context.moveTo(256 + 0.5, y + 0.5);
        for (var i = 1; i < 256; ++i) {
            context.lineTo(256 + i + 0.5, values[(valuePointer - i) & 255] + 0.5);
        }
        context.stroke();
    }

    (function frame() {
        canvas.width = canvas.clientWidth;
        canvas.height = canvas.clientHeight;
        x = 144.0;
        y = 128.0;
        switch (waveformInput.value) {
            case "square":
                for (var order = 0; order <= orderInput.value; order++) {
                    fourier((order << 1) + 1);
                }
                break;
            case "sawtooth":
                for (var order = 1; order <= orderInput.value; order++) {
                    fourier(order << 1);
                }
                break;
        }
        connect();
        drawWave();
        var now = new Date().getTime();
        time += (now - startTime) * Math.pow(10.0, frequencyInput.value);
        startTime = now;
        window.requestAnimationFrame(frame);
    })();
</script>